/**
 * Copyright 2007 Igor Chaves Cananéa
 *
 * This file is part of PBSFW
 *
 * PBSFW is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * PBSFW is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#ifndef _SIMULATOR_H_
#define _SIMULATOR_H_

#include <set>
#include <vector>
#include "defines.h"
#include "simulatorinterface.h"
#include "defines.h"
#include "resetable.h"
#include "propertyholder.h"
#include "property.h"
using namespace std;

namespace pbsfw {
    class Process;
    class Scheduler;
    /**
    * This class represents the simulation framework entry point for the client
    * code. Since there should be only one instance of the framework for each
    * application, it is implemented as a singleton. An <b><i>extern</i></b>
    * declartion is used to export a reference from the single instance of the
    * simulator. All code that needs to access simulator methods should use the
    * sim reference to do so.
    *
    * Example:
    * <b>sim.schedule(event)</b>
    *
    *
    * In a typical scenario, the schedule() method will not be used by the
    * client code. Applications should prefer the Actor::unicastCommunciation(),
    * Actor::broadcastCommunication() and Actor::selfCommunication() methods
    * that are present in the Actor interface to schedule messages in the
    * simulation.
    */

    class Simulator : public SimulatorInterface {
        private:
            static Simulator *simulator;
            uint32_t replications;
            Scheduler *sched;
            simtime_t stopTime;
            set<Process*> &passive;
            vector<pair<Process*, simtime_t> > &initProcesses;
            vector<Resetable*> &resetables;
            vector<PropertyHolder*> &propertyHolders;
            PropertyTable &properties;
            PropertyNameTable &propertyNames;
            bool started;

            Simulator();
            void reset();
            void init(simtime_t until);
            void clear();

            void buildPropertyTable();
            void signalSimulationEnd();

        public:
            ~Simulator();
            /**
            * Returns the simulator instance. Use is discouraged; use the
            * <b>sim</b> reference instead.
            */
            static Simulator &getInstance() {
                return *Simulator::simulator;
            }

            void iAmResetable(Resetable *r) {
                this->resetables.push_back(r);
            }

            void iHaveProperties(PropertyHolder *ph) {
                this->propertyHolders.push_back(ph);
            }

            PropertyTable queryProperties(vector<string> &names);

            simtime_t now() const;
            void activate(Process &p, simtime_t at);
            void reactivate(Process &p, simtime_t at);
            void hold(Process &p, simtime_t howMuch);
            void passivate(Process &p);

            void run(uint32_t replications, simtime_t until);
    };

    #ifndef _CORE_SIMULATOR_
        #ifdef _INTERNAL_CLASS_
            /** A reference to the Simulator instance. */
            extern Simulator &sim;
        #endif
    #endif
};


#endif
